\documentclass[a4paper,10pt]{article}
\usepackage[spanish]{babel} %Paquetes de idioma
\usepackage[latin1]{inputenc} %Paquetes de idioma
\usepackage{graphicx} % Paquete para ingresar gráficos
\usepackage{hyperref}
\usepackage{fancybox}

%Encabezado y Pié de página
\input{EncabezadoyPie.tex}
%Carátula del Trabajo
\title{ \input{Portada.tex} }

\begin{document}
	\maketitle %Hace que el título anterior sea el principal del documento
	\newpage

	\tableofcontents %Esta línea genera un indice a partir de las secciones y subsecciones creadas en el documento
	\newpage

	\section{Resumen}
		El objetivo del presente trabajo consiste en realizar una aplicaci\'on en lenguaje C, la cual debe implementar, ademas, una serie de funciones en \emph{assembly}, usando como herramientas el set de instrucciones basado en la arquitectura MIPS y el concepto de ABI exhibido por la c\'atedra.
		\vspace{1cm}
 	\section{Cambios en la reentrega}
 	Para la reentrega se realizaron los cambios pertinentes en el codigo para que la aplicaci\'on pase el test que provee la catedra. Para esto tambi\'en se debi\'o adaptar el script de test a netBSD, para esto se tuvo que reemplazar el comando \emph{od} por la aplicaci\'on del TP 0 el cual pasaba el test. Tambi\'en se han cambiado algunos parametros de los comandos utilizados que no estan soportados en este Sistema Operativo.\\
 	\indent El script de prueba modificado se incluye en el cd con el c\'odigo del Trabajo Pr\'actico.
\section{Compilaci\'on y Ejecuci\'on}
	\subsection{Compilaci\'on}
		Para compilar el programa se provee en el CD un archivo Makefile para compilarlo. Para esto desde una terminal en el directorio donde se encuetra el archivo Makefile se debe ejecutar el comando:
	\begin{verbatim}
		$ make
	\end{verbatim}
	Esto generara el archivo ejecutable tp1 y el archivo ejecutable tp0 para poder utilizarlo al correr el script de prueba.
	\subsection{Ejecuci\'on}
		Para ejecutar el programa, una vez compilado, desde la misma terminal se ejecuta el archivo tp0 con los parametros deseados, un ejemplo de ejecuci\'on es:
	\begin{verbatim}
		$ ./tp1 -i /dev/urandom -l 12 -d " "
	\end{verbatim}

	\newpage
	\section{Ap\'endice}
	\subsection{C\'odigo Fuente}

	\subsubsection{main.c}
		\begin{verbatim}
		/*
 ============================================================================
 Name        : main.c
 Author      : Agustin Pivetta, Diego Rivas
 Version     : 1.0
 Description : Tp1 66.20: Dump Octal en MIPS
 ============================================================================
 */

#include <stdbool.h>
#include <string.h>
#include <getopt.h>
#include "odump.h"

//Extras hasta que se complete el desarrollo en assembly @BORRAR
#include <unistd.h>

#define MIN_NUM_ASCII 48
#define MAX_NUM_ASCII 57

/*
 * Imprimo la ayuda
 */
void printHelp() {
	printf(
			"Usage:\n"
					"  tp1 -h\n"
					"  tp1 -V\n"
					"  tp1 [options]\n"
					"Options:\n"
					"  -h, --help      Print this information.\n"
					"  -V, --version   Print version and quit.\n"
					"  -O, --octal     Octal dump mode: encode the input (binary file) in \n"
					"                  octal format (default).\n"
					"  -b, --binary    Binary mode: decode the input (octal dump file) in\n"
					"                  binary format.\n"
					"  -i, --input     Path to input file (defaults to stdin).\n"
					"  -o, --output    Path to output file (defaults to stdout).\n"
					"  -l, --length    Number of octal bytes per line (line length) at the\n"
					"                  output in octal dump mode (0 = default = no limit).\n"
					"  -d, --delimiter Delimiter between octal bytes. Could be a string, not\n"
					"                  only a single byte. Used as delimiter for octal output\n"
					"                  and input files (default: none).\n");

}

/*
 * Imprimo la version
 */
void printVersion() {
	printf("66.20 - TP1: Octal Dump, Versión 1.00\n");
	return;
}
/*
 * Parseo number y si es un numero entero valido lo casteo a entero
 */
size_t parseInt(const char* number) {
	char len = strlen(number);

	// Si me envían un cero devuelvo 0
	if ((len == 1) && (number[0] == '0'))
		return 0;

	size_t i = 0;
	// Recorro a m y verifico que cada uno de sus caracteres sean números
	for (i = 0; i < len; i++) {
		if ((number[i] < MIN_NUM_ASCII) || (number[i] > MAX_NUM_ASCII))
			return 0;
	}

	// Si m es una cadena válida la convierto en un número
	unsigned int aux = 0;
	sscanf(number, "%u", &aux);
	return aux;
}

/*
 * Abro los archivos de input/outout
 */
FILE* openFile(char* fileName, FILE* default_file, const char* mode) {
	FILE* fd;
	if (fileName == NULL ) {
		fd = default_file;
	} else {
		fd = fopen(fileName, mode);
	}

	if (!fd) {
		fprintf(stderr, odump_errmsg[3], fileName);
		exit(-1);
	}
	return fd;
}

int main(int argc, char** argv) {
	// Fuerzo que un error en getopt no se imprima por stderr
	opterr = 0;
	// Indica cuando terminé de procesar todos los argumentos
	int nextOpt = 0;
	// Cadena que lista las opciones cortas válidas
	const char* const opCortas = "hVObi:o:l:d:";
	// Estructura de structs describiendo los valores largos
	const struct option opLargas[] = { { "help", no_argument, NULL, 'h' }, {
			"version", no_argument, NULL, 'V' }, { "octal", no_argument, NULL,
			'O' }, { "binary", no_argument, NULL, 'b' }, { "input",
			optional_argument, NULL, 'i' }, { "output", optional_argument, NULL,
			'o' }, { "length", optional_argument, NULL, 'l' }, { "delimiter",
			optional_argument, NULL, 'd' }, { NULL, no_argument, NULL, 0 } };

	// Variable donde recibo el nombre del archivo de entrada
	char* inputFName = NULL;
	// Variable donde recibo el nombre del archivo de salida
	char* outputFName = NULL;
	// Variable donde guardo el modo de ejecucion (1:octal dump mode | 2: binary dump mode)
	char mode = 1;
	// Variable donde guardo la longitud de numeros octal por linea
	size_t length = 0;
	// Variable donde guardo el delimitador de bytes octales
	char* delimiter = NULL;
	//Retorno de las funciones de encode y decode
	int rc = 0;

	while (1) {
		//Leo que me devuelve get_opt
		nextOpt = getopt_long(argc, argv, opCortas, opLargas, NULL );

		if (nextOpt == -1) {
			break;
		}

		switch (nextOpt) {
		case 'h': {
			printHelp();
			return 0;
			break;
		}

		case 'V': {
			printVersion();
			return 0;
			break;
		}

		case 'O': {
			mode = 1;
			break;
		}

		case 'b': {
			mode = 2;
			break;
		}

		case 'i': {
			if (strcmp(optarg, "-") != 0) {
				inputFName = malloc(strlen(optarg) + 1);
				strcpy(inputFName, optarg);
			}
			break;
		}

		case 'o': {
			if (strcmp(optarg, "-") != 0) {
				outputFName = malloc(strlen(optarg) + 1);
				strcpy(outputFName, optarg);
			}
			break;
		}

		case 'l': {
			length = parseInt(optarg);
			break;
		}

		case 'd': {
			delimiter = malloc(strlen(optarg) + 1);
			strcpy(delimiter, optarg);
			break;
		}

		default: {
			printHelp();
			exit(-1);
			break;
		}
		}
	}

	FILE* input = openFile(inputFName, stdin, "rb");
	FILE* output = openFile(outputFName, stdout, "w");

	//Obtengo los file descriptors
	int infd = fileno(input);
	int outfd = fileno(output);

	//Seteo las opciones
	struct odump_opts opts = { length, delimiter };

	if (mode == 1) {
		rc = odump_encode(infd, outfd, &opts);
	} else if (mode == 2) {
		rc = odump_decode(infd, outfd, &opts);
	}

	fclose(input);
	fclose(output);

	if (rc != 0) {
		fprintf(stderr, "%s", odump_errmsg[rc]);
        return rc;
	}

	return 0;
}

		\end{verbatim}
	\subsubsection{odump.h}
		\begin{verbatim}
		/*
 ============================================================================
 Name        : odump.h
 Author      : Agustin Pivetta, Diego Rivas
 Version     : 1.0
 Description : Tp1 66.20: funciones y estructuras de datos auxiliares para 
	       realizar los cómputos de encoding y decoding
 ============================================================================
*/ 
#include <stdio.h>
#include <stdlib.h>

//Estructura de opciones
struct odump_opts {
	size_t length;
	const char* delim;
};

//Vector de mensages de error
extern const char* odump_errmsg[];

//Prototipos de funciones
int odump_encode(int infd, int outfd, struct odump_opts *opts);
int odump_decode(int infd, int outfd, struct odump_opts *opts);
void bin2oc(unsigned char bin, char* octal);
unsigned char oc2bin(char octal[]);
size_t write_asm(int fildes, const void *buf, size_t nbytes);
ssize_t read_asm(int fildes, void *buf, size_t nbyte);
size_t strlen_asm(const char *str);

		\end{verbatim}
	\subsubsection{odump.S}
		\begin{verbatim}
		//######################################################################
//const char *odump_errmsg[];
        .rdata
        .align  2
$msg0:
        .asciiz  "OK"
$msg1:
        .asciiz  "No se pudo leer el archivo\n"
$msg2:
        .asciiz  "No se pudo escribir en el archivo\n"
$msg3:
        .asciiz  "Error al intentar abrir el archivo %s\n"
$msg4:
        .asciiz  "Error en el archivo octal, byte mayor a 255\n"

        .globl  odump_errmsg
        .data
        .type   odump_errmsg, @object
        .size   odump_errmsg, 20
odump_errmsg:
        .word   $msg0
        .word   $msg1
        .word   $msg2
        .word   $msg3
	.word   $msg4

//######################################################################
//void bin2oc(unsigned char bin, char* octal) {

// Diagrama de Stack
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

#include <mips/regdef.h>
#include <sys/syscall.h>

.data
    end:  .ascii  "\0"

.text
.align 2


.globl bin2oc
.ent bin2oc

bin2oc:
	.frame $fp,24,ra
        .set noreorder
        .cpload t9
        .set reorder

	// creo stack frame
        subu sp,sp,24           #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 0

        sw $fp,20(sp)           #Respaldo la posicion del frame pointer
        sw gp,16(sp)            #Respaldo la posicion del global pointer
        move $fp,sp

        sw a0,24(sp)            #bin
        sw a1,28(sp)            #octal

        li t0,2                 #i = 2, contador del loop
        li t1,8                 #divisor = 8, constante
        move t2,a0              #t2 = bin = dividendo

$FOR:
        div t2,t1               #LO = t2/t1 y HI = t2 mod t1
        mflo t2                 #Dividendo = Cociente
        mfhi t3
        addi t3,t3,48           #resto + 48 forma el ascii del numero
        addu t4,a1,t0           #puntero a vector "octal" + offset
        sb t3,0(t4)             #octal[i] = ascii
        addi t0,t0,-1           #Resto 1 al contador
        bgez t0,$FOR

        lw $fp,20(sp)           #Recupero la posicion del frame pointer
        lw gp,16(sp)            #Recupero la posicion del global pointer
        addu sp,sp,24
        j ra
.end bin2oc

//######################################################################
//unsigned char oc2bin(char octal[]) {

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl oc2bin
.ent oc2bin

oc2bin:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento en el ABA del caller
    
    	lbu t0,0(a0)    # cargo el primer char del octal[3]
    	subu t0,t0,48
        bgt t0,7,oc2bin_error
        blt t0,0,oc2bin_error
    	sll t0,t0,6     # t0 = t0*64 = t0*(2^6) 
        
    	lbu t1,1(a0)    # cargo el segundo char del octal[3]
    	subu t1,t1,48
        bgt t1,7,oc2bin_error
        blt t1,0,oc2bin_error
    	sll t1,t1,3     # t0 = t0*8 = t0*(2^3) 
        
    	lbu t2,2(a0)    # cargo el tercer char del octal[3]
    	subu t2,t2,48
        bgt t2,7,oc2bin_error
        blt t2,0,oc2bin_error
    	sll t2,t2,0     # t0 = t0*1 = t0*(2^0) 
        
    	addu v0,t0,t1
    	addu v0,v0,t2

        blt v0,256,oc2bin_ok	#Verifica que los bytes sean menores que 256  
        
oc2bin_error:
	//hubo error en el byte
	li v1,4			#Cargo el codigo de error
	li v0,0			#Vacio el resultado

oc2bin_ok:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end oc2bin

//######################################################################
//Esta funcion es la equivalente a write() de C
//size_t write_asm(int fildes, const void *buf, size_t nbytes);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl write_asm
.ent write_asm

write_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_write # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,write_error	#Ocurrio un error en la escritura
	beq a3,zero,write_exit
    
write_error:

	move v0,zero
	addi v1,zero,2	#Pongo a v1 en 2 para indicar error de escritura

write_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end write_asm

//######################################################################
//Esta funcion es la equivalente a read() de C
//ssize_t read_asm(int fildes, void *buf, size_t nbyte);

// Diagrama de Stack
// 32   [  a2  ]
// 28   [  a1  ]
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl read_asm
.ent read_asm

read_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
        // creo stack frame
        subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
        .cprestore 16
    
        sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
        sw a0,24($fp)	# guardo el argumento 0 en el ABA del caller
	sw a1,28($fp)	# guardo el argumento 1 en el ABA del caller
	sw a3,32($fp)	# guardo el argumento 2 en el ABA del caller
    
	li v0,SYS_read # Los argumentos ya vienen cargados en el correcto orden desde la llamada
	syscall

	bne a3,zero,read_error	#Ocurrio un error en la lectura
	beq a3,zero,read_exit
    
read_error:

	move v0,zero
	addi v1,zero,1	#Pongo a v1 en 1 para indicar error en la lectura	

read_exit:
	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end read_asm

//######################################################################
//Esta funcion es la equivalente a strlen() de C
//size_t strlen_asm(const char *str);

// Diagrama de Stack
// 24 __[__a0__]_____________________
// 20 - [  fp  ] Saved Register Area
// 16 - [  gp  ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl strlen_asm
.ent strlen_asm

strlen_asm:
        .frame $fp,24,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,24   #Reservo 24 Bytes (16 para parametros, 8 para respaldar los valores de fp y gp)
    .cprestore 16
    
    sw $fp,20(sp) 	#Respaldo la posicion del frame pointer
	sw gp,16(sp)	#Respaldo la posicion del global pointer
	move $fp,sp
    sw a0,24($fp)	#guardo el argumento 0 en el ABA del caller
    
	move v0,zero    #inicializo en cero el retorno
	lb t1,end	#Cargo el "\0"

$LOOP:
	add t2,a0,v0	#inicio de vector + len
	lb t0,0(t2)	#Leo una letra
	addi v0,v0,1	#incrementa la longitud
	bne t0,t1,$LOOP

	addi v0,v0,-1	#Hay que restar 1 al resultado porque no se cuenta al \0 como letra

	lw $fp,20(sp)	#Recupero la posicion del frame pointer
	lw gp,16(sp)	#Recupero la posicion del global pointer
	addu sp,sp,24
	j ra
.end strlen_asm


//######################################################################
//int odump_encode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 48 - [  a2  ]
// 44 - [  a1  ]
// 40 - [__a0__]_____________________
// 36 - [  pad ]
// 32 - [  ra  ] Saved Register Area
// 28 - [  fp  ]
// 24 - [  gp  ]_____________________
// 20 - [      ] Local Temp Area
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_encode
.ent odump_encode

odump_encode:
    .frame $fp,40,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,40   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 24
    
    sw ra,32(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,28(sp)   #Respaldo la posicion del frame pointer
	sw gp,24(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,40($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,44($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,48($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
    //leo de infd por primera vez
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

$WHILE_ENCODE:
    //paso el binario a octal
    lbu a0,bin_encode(zero)
    la a1,oc_encode
    jal bin2oc
    
    //escribo en outfd
    lw a0,44($fp)
    la a1,oc_encode
    li a2,3
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,3
    bne t0,v0,$OUT_ENCODE # si no escribio 3 caracteres salgo
    
    // chequeo si tengo que escribir un salto de linea
    lw t0, 16($fp)  # levanto el contador de bytes octales
    addiu t0,t0,1   # le sumo 1
    sw t0, 16($fp)  # guardo el contador de bytes octales
    lw t1,48($fp)   # levanto el puntero al struct
    lw t2,0(t1)  # leo el valor de length
    bne t0,t2,$DELIM_ENCODE
    
    //escribo un salto de linea
    //antes leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    lw a0,44($fp)
    la a1,endline_encode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no escribio 1 caracter salgo
    
    li t0,0      #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    b $WHILE_ENCODE
    
$DELIM_ENCODE:
    //leo de infd para ver si llegue al eof
    lw a0,40($fp)
    la a1,bin_encode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_ENCODE # si no leyo 1 caracter salgo

    //escribo el delimitador
    lw t0,48($fp)               # levanto el puntero al struct
    lw a0,4(t0)                 # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_ENCODE       # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm              # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)               # guardo el largo del delimitador
    beqz v0,$WHILE_ENCODE
    lw a0,44($fp)
    lw t0,48($fp)               # levanto el puntero al struct
    lw a1,4(t0)                 # cargo el puntero al delimitador en a1
    addiu a2,v0,0               # cargo el valor devuelto por strlen_asm en a2
    jal write_asm
    bne v1,zero,$ERR_ENCODE	#Verifica errores
    lw t0,20($fp)               # levanto el largo del delimitador
    bne t0,v0,$OUT_ENCODE       # si no escribio el delimitador salgo
    b $WHILE_ENCODE

$ERR_ENCODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_ENCODE

$OUT_ENCODE:
        lw ra,32(sp)	#Recupero la posicion del return address
	lw $fp,28(sp)	#Recupero la posicion del frame pointer
	lw gp,24(sp)	#Recupero la posicion del global pointer
	addu sp,sp,40
	j ra
.end odump_encode

    
    
//######################################################################
//int odump_decode(int infd, int outfd, struct odump_opts *opts);

// Diagrama de Stack
// 56 - [  a2  ]
// 52 - [  a1  ]
// 48 - [__a0__]_____________________
// 44 - [  pad ]
// 40 - [  ra  ] Saved Register Area
// 36 - [  fp  ]
// 32 - [  gp  ]_____________________
// 28 - [      ] 
// 24 - [      ] Local Temp Area
// 20 - [      ] 
// 16 - [      ]_____________________
// 12 - [      ] 
// 8  - [      ] Argument Building
// 4  - [      ] Area
// 0  - [      ]_____________________

.globl odump_decode
.ent odump_decode

odump_decode:
    .frame $fp,48,ra
	.set noreorder
	.cpload t9
	.set reorder
    
    // creo stack frame
    subu sp,sp,48   #Reservo 40 Bytes (16 para parametros, 16 para respaldar los valores de fp, gp y ra)
    .cprestore 32
    
    sw ra,40(sp)	#Respaldo el ra, la funcion es non-leaf
    sw $fp,36(sp)   #Respaldo la posicion del frame pointer
	sw gp,32(sp)	#Respaldo la posicion del global pointer
    
	move $fp,sp
    
    sw a0,48($fp)	#guardo el argumento 0 en el ABA del caller
    sw a1,52($fp)	#guardo el argumento 1 en el ABA del caller
    sw a2,56($fp)	#guardo el argumento 2 en el ABA del caller
    
    move v1,zero	#inicializo v1 en cero para luego detectar errores
    li t0,0         #inicializo el contador en 0
    sw t0,16($fp)   #lo guardo en la LTA
    
$WHILE_DECODE:
la t0,oc_decode
sb zero,0(t0)
sb zero,1(t0)
sb zero,2(t0)


//leo de infd
	lw a0,48($fp)
    la a1,oc_decode
    li a2,1
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no leyo 1 caracteres salgo
    
    lb t0,oc_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    //leo los 2 caracteres que me faltan
	lw a0,48($fp)
    la t0,oc_decode
    addi a1,t0,1    # leo a partir de oc_decode[1]
    li a2,2
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,2
    bne t0,v0,$OUT_DECODE # si no leyo 2 caracteres sigo

$SIGO:
    //paso el octal a binario
    la a0,oc_decode
    jal oc2bin
    bne v1,zero,$ERR_DECODE	#Verifica errores
    sw v0,bin_decode
    
    //escribo en outfd
    lw a0,52($fp)
    la a1,bin_decode
    li a2,1
    jal write_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE # si no escribio 1 caracter salgo
    
$DELIM_DECODE:
    //leo el delimitador
    lw t0,56($fp)           # levanto el puntero al struct
    lw a0,4(t0)             # cargo el puntero al delimitador en a0
    beqz a0,$WHILE_DECODE   # chequeo si la direccion del delimitador es 0 (NULL) 
    jal strlen_asm          # llamo a strlen para obtener el largo del delimitador
    sw v0,20($fp)           # guardo el largo del delimitador
    beqz v0,$WHILE_DECODE   # si el largo es 0 paso
    
    li t0,0
    sw t0,24($fp)
$READ_DELIM:
    lw a0,48($fp)
    la a1,void_decode       # leo en void
    li a2,1                 # leo 1 byte
    jal read_asm
    bne v1,zero,$ERR_DECODE	#Verifica errores
    li t0,1
    bne t0,v0,$OUT_DECODE   # si no leyo 1 byte salgo
    
    lw t0,void_decode(zero)
    beq t0,10,$WHILE_DECODE # si lei un \n (10 en bytes) sigo
    
    lw t0,24($fp)           # levanto el contador
    addiu t0,t0,1           # incremento el contador
    sw t0,24($fp)           # guardo el contador incrementado
    lw t1,20($fp)           # levanto el largo del delimitador
    bgt t1,t0,$READ_DELIM
    li t0,0                 # reinicio a 0 el contador
    sw t0,24($fp)           # guardo el contador
    b $WHILE_DECODE

$ERR_DECODE:
    move v0,v1 		#Muevo el codigo de error al retorno de la funcion y salgo de esta
    b $OUT_DECODE

$OUT_DECODE:
    lw ra,40(sp)	#Recupero la posicion del return address
	lw $fp,36(sp)	#Recupero la posicion del frame pointer
	lw gp,32(sp)	#Recupero la posicion del global pointer
	addu sp,sp,48
	j ra
.end odump_decode

.data

    endline_encode: .ascii "\n"
    bin_encode: .byte 0
    oc_encode: .byte 0,0,0,0

    endline_decode: .ascii "\n"
    bin_decode: .byte 0
    oc_decode: .byte 0,0,0
    void_decode: .byte 0

		\end{verbatim}
	\subsubsection{Makefile}
		\begin{verbatim}
		CFLAGS=-g -Wall -lm -std=c99
EXEC0= tp0
EXEC1= tp1
CC=gcc

all:	$(EXEC0) $(EXEC1)

$(EXEC0): tp0.c
	$(CC) $(CFLAGS) tp0.c -o $(EXEC0)

$(EXEC1): main.c
	$(CC) $(CFLAGS) main.c *.S -o $(EXEC1)

		\end{verbatim}
	% Citas bibliográficas.
	\begin{thebibliography}{99}
		\bibitem{proc} Informaci\'on adicional sobre MIPS, http://es.wikipedia.org/wiki/MIPS\_(procesador).
		\bibitem{tech} MIPS Technologies, http://www.mips.com/.
		\bibitem{abi} MIPS ABI, http://refspecs.freestandards.org/elf/mipsabi.pdf/.
	\end{thebibliography}
\end{document}


